#!/usr/bin/python
# -*- coding: utf8 -*-

from pyparsing import Forward, Literal, Keyword, ParserElement, Regex

# Estas son los dos bloques básicos de la sintaxis de todo lambda cálculo.
# Se definen aquí para poder referenciarlos al definir la sintaxis de las
# extensiones que se construyan a continuación.

TipoExp = Forward()
Exp = Forward()


Id = Regex('[A-Z][a-zA-Z]*')

def makeSintaxis(*args):
    """
    Helper para construir sintaxis simples
    Considera a las palabras (solo letras) como keywords,
    a las palabras con simbolos como literal (en ambos casos con suppress)
    y a las expresiones las mantiene como estaban.
    """
    if len(args) == 0:
        raise TypeException("la sintaxis no puede ser nula")
    
    res = None
    for each in args:
        if res == None:
            res = convertir(each)
        else:
            res = res + convertir(each)
    return res

def convertir(obj):
    if isinstance(obj,basestring):
        
        lits = obj.split(" ")
        res = None
        for each in lits:
            if not (each == ""):
                if res == None:
                    if obj.isalpha():
                        res = Keyword(each).suppress()
                    else:    
                        res = Literal(each).suppress()
                else:
                    if obj.isalpha():
                        res = res + Keyword(each).suppress()
                    else:    
                        res = res + Literal(each).suppress()
        if res == None:
            raise TypeError(("String invalido %s"% obj))
        return res                    
    elif isinstance(obj, ParserElement ):
        return obj
    else:
        raise "tipo inesperado para una sintaxis"
    
        


#############################################
# Clase ParseableObject                     #
#############################################

class ParseableObject(object):
    def __init__(self,tok):
        """ Construye un Parseable Object a partir de una lista de tokens """
        pass
    
    @classmethod
    def parseAction(cls,s,l,tok):
        """ Construye el objeto luego de que fue parseado """
        return cls(tok)
    
    @classmethod
    def sintaxis(cls):
        """ Define la sintaxis que va a tener la expresion """
        raise NotImplementedError()
        
    

        
#############################################
# Clase Expresion                           #
#############################################

class Expresion(ParseableObject):
    
    def tipar(self):
        """ Establece las reglas para el tipado de la expresion """
        raise NotImplementedError()
    
    def reducir(self):
        """ Establece las reglas semantica para la reduccion de las expresiones """
        raise NotImplementedError()

    def sustituir(self, var, expresion):
        """ Sustituye las apariciones de una variable por una expresion"""
        raise NotImplementedError()
    
    def __repr__(self):
        return "<%s object at %s>" % (self.__class__.__name__, hex(id(self)))
    

def atomoFactory(sintaxis, tipo):
    """
    Construye una clase representativa del átomo <sintaxis>
    de tipo <tipo> para evitar código repetitivo en las definiciones
    de átomos.
    """
    class c(Expresion): pass
    
    tipar = lambda self, ns: tipo([])
    reducir = lambda self: self
    sustituir = lambda self, var, exp: self
    __repr__ = lambda self: "<Atomo %s de tipo %s>" % (sintaxis, tipo([]))
    __str__ = lambda self: sintaxis
    
    setattr(c, 'tipar', tipar)
    setattr(c, 'reducir', reducir)
    setattr(c, 'sustituir', sustituir)
    setattr(c, '__repr__', __repr__)
    setattr(c, '__str__', __str__)
    
    c.sintaxis = classmethod(lambda cls: Literal(sintaxis).suppress())
    
    return c



#############################################
# Clase Tipo                                #
#############################################
    
class Tipo(ParseableObject):
    def __eq__(self, other):
        """
        Verifica si este tipo es igual a otro. Como los tipos se crean 
        subclaseando Type, un tipo es igual a otro si es de la misma clase.
        """
        return self.__class__ == other.__class__
        

class TypeException(Exception):
    pass

class SyntaxException(Exception):
    pass

def simpleTypeFactory(nombre):
    """
    Construye una clase representativa de un nuevo tipo atómico
    (no paramétrico) de nombre <nombre> para evitar código repetitivo
    en las definiciones de tipos simples.
    """
    class d(Tipo):
        pass
   
    __repr__ = lambda self: "<Tipo %s>" % nombre
    __str__ = lambda self: nombre
        
    setattr(d, '__repr__', __repr__)
    setattr(d, '__str__', __str__)
    
    def sintaxis(cls):
        t = Literal(nombre)
        return t
    
    d.sintaxis = classmethod(sintaxis)
    
    
    ##d.parseAction = classmethod(parseAction)
    
    return d




    
